/*
* @(#)TransUtil.java 2.0 11/11/23
*
* Copyright  2011-2011 Gopay Information and technology Co., Ltd.
* Sanyunqiao, Chaoyang, Beijing, 100027, China.
* All Rights Reserved.
*
* This software is the confidential and proprietary information of
* Gopay Information and technology Co., Ltd. (“Confidential Information”).
* You shall not disclose such Confidential Information and shall use it only in
* accordance with the terms of the license agreement you entered into with Gopay.
*/

package com.gopay.common.util;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.regex.Pattern;

import javax.xml.crypto.dsig.TransformException;

import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.FileSystemResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.StringUtils;



/**
 * TransUtil.java
 * <p>
 * Created on Oct 30, 2007 Modification history
 * <p>
 * Bankswitch模块的工具类.
 *
 * @author Richard Chen
 * @version 1.0
 * @since 1.0
 */

public abstract class TransUtil {

	/**
	 * No instantiation.
	 */
	protected TransUtil() {
		throw new UnsupportedOperationException();
	}


	/**
	 * Get date string in a yyyyMMddhhmmssSSS style, like 20071126091234111
	 * means Nov.26, 2007 09:12:34:111.
	 *
	 * @return date string in a yyyyMMddhhmmssSSS style.
	 */
	public static String getSendTime() {
		return getTimeString(TIME_FORMAT_YEAR_MILLISECOND);
	}

	/**
	 * Get date string in a yyyyMMddHHmmss style, like 20071126091234 means
	 * Nov.26, 2007 09:12:34.
	 *
	 * @return date string in a yyyyMMddHHmmss style.
	 */
	public static String getTransTime() {
		return getTimeString(TIME_FORMAT_YEAR_SECOND);
	}

	/**
	 * Get date for bank CITIC.
	 *
	 * @return .
	 */
	public static String getDateForCITIC() {
		return getTimeString(TIME_FORMAT_YEAR_Y);
	}

	/**
	 * 生成yyMMddhhmmssSSS格式的时间字符串，工行报文PackageID字段用.
	 *
	 * @return yyMMddhhmmssSSS格式的时间字符串.
	 */
	public static String getPackageID() {
		StringBuffer stringBuffer = new StringBuffer(
				getTimeString(TIME_FORMAT_TWO_BIT_YEAR_SECOND));
		int tmp = 0;
		synchronized (LOCK) {
			suffix %= LENGTH;
			tmp = suffix;
			suffix++;
		}
		if (tmp < TWO_BIT) {
			stringBuffer.append(SUFFIX_TWO_ZERO).append(tmp);
		} else if (tmp < THREE_BIT) {
			stringBuffer.append(SUFFIX_ONE_ZERO).append(tmp);
		} else {
			stringBuffer.append(tmp);
		}
		return stringBuffer.toString();
	}

	/**
	 * 生成yyyy-MM-dd HH:mm:ss格式的时间字符串，民生报文dtClient字段用.
	 *
	 * @return yyyy-MM-dd HH:mm:ss格式的时间字符串.
	 */
	public static String getDtClient() {
		return getTimeString(TIME_FORMAT_YEAR_SECOND_G);
	}

	/**
	 * 生成yyyy-MM-dd格式的时间字符串，民生报文时间字段用.
	 *
	 * @return yyyy-MM-dd格式的时间字符串.
	 */
	public static String getCMBCDate() {
		return getTimeString(TIME_FORMAT_YEAR_G);
	}

	/**
	 * 取得时间字符串.
	 *
	 * @param timeFormat
	 *            指定时间字符串的格式, 参考{@link java.text.SimpleDateFormat}.
	 * @return 以timeFormat格式返回时间.
	 */
	private static String getTimeString(String timeFormat) {
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat(timeFormat);
		String sendTime = simpleDateFormat.format(new Date());
		return sendTime;
	}

	/**
	 * 对一金额字符串进行去小数点操作, 等于对输入金额放大一百倍, 返回金额字符串不能包括小数点.
	 * 如amountConvert2Fen("100.5")则返回"10050".
	 *
	 * @param amountStr
	 *            必须遵循正则表达式<code>^(\\d{1,10}(\\.\\d{0,2})?)$</code>, 否则抛出<code>IllegalArgumentException</code>异常.
	 * @return 返回金额字符串为原金额的一百倍.
	 */
	public static String amountConvert2Fen(String amountStr) {

		if (!PATTERN.matcher(amountStr).matches()) {
			throw new IllegalArgumentException(amountStr + "输入的金额格式有误，无法转化金额为分");
		}
		BigDecimal originalAmount = new BigDecimal(amountStr);
		String resultStr = originalAmount.multiply(ONEHUNDRED).setScale(0)
				.toString();
		return resultStr;
	}
	 /**
     * 对一金额字符串进行加小数点操作, 等于对输入金额缩小一百倍
     * 如amountConvert2Fen("1")则返回"0.01".
     * 
     * @param amountStr
     *            必须遵循正则表达式<code>^(\\d{1,10}(\\.\\d{0,2})?)$</code>, 否则抛出
     *            <code>IllegalArgumentException</code>异常.
     * @return 返回金额字符串为原金额的0.01
     */
    public static String amountConvert2Yuan(String amountStr) {

        if (!PATTERN.matcher(amountStr).matches()) {
            throw new IllegalArgumentException(amountStr + "输入的金额格式有误，无法转化金额为分");
        }
        BigDecimal originalAmount = new BigDecimal(amountStr);
        String resultStr = originalAmount.divide(ONEHUNDRED).toString();
        return resultStr;
    }
    /**
     * 
     * 功能描述：对金额进行操作，返回定长金额，不够位数左补字符
     *
     * @param amountStr
     * @param size
     * @param padChar
     * @return
     * 
     * @author 宣广海
     *
     * @since 2017年11月15日
     *
     * @update:[变更日期YYYY-MM-DD][更改人姓名][变更描述]
     */
    public static String amountConvert2Fen(String amountStr,int size,String padChar) {

    	if (!PATTERN.matcher(amountStr).matches()) {
			throw new IllegalArgumentException(amountStr + "输入的金额格式有误，无法转化金额为分");
		}
		BigDecimal originalAmount = new BigDecimal(amountStr);
		String resultStr = originalAmount.multiply(ONEHUNDRED).setScale(0)
				.toString();
        return org.apache.commons.lang3.StringUtils.leftPad(resultStr, size, padChar);
    }
	/**
	 * 生成yyyyMMddhhmmssSSS格式时间字符串，工行报文SignTime字段用.
	 *
	 * @return yyyyMMddhhmmssSSS格式的时间字符串.
	 */
	public static String getSignTime() {
		return getTimeString(TIME_FORMAT_YEAR_MILLISECOND);
	}



	/**
	 * 民生银行读取证书文件.
	 *
	 * @param filename
	 *            文件名.
	 * @return 证书文件二进制流.
	 * @throws Exception
	 *             读取出错.
	 */
	public static byte[] readCertFileForCMBCB2C(String filename)
			throws Exception {
		try {
			FileInputStream fileInStream = new FileInputStream(filename);
			ByteArrayOutputStream fileByteStream = new ByteArrayOutputStream();
			int i = 0;
			while ((i = fileInStream.read()) != -1) {
				fileByteStream.write(i);
			}
			fileInStream.close();
			return fileByteStream.toByteArray();
		} catch (Exception e) {
			throw new Exception(e.getMessage());
		}

	}



	/**
	 * 按指定的格式将<code>java.util.Date</code>转换为字符串.
	 *
	 * @param dateFormat
	 *            指定日期字符串的格式, 参考{@link java.text.SimpleDateFormat}.
	 * @param date
	 *            需转换格式的日期.
	 * @return 指定格式的日期字符串.
	 */
	public static String getDateStr(String dateFormat, Date date) {
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat);
		String dateStr = simpleDateFormat.format(date);
		return dateStr;
	}

	/**
	 * 按yyyyMMdd格式取当前日期的字符串.
	 *
	 * @return 当前日期的字符串.
	 */
	public static String getCurrentDateStr() {
		return getDateStr(CURRENT_DATE, new Date());
	}

	/**
	 * 将62以下数字转换成个位数+字母.
	 *
	 * @param numStr numStr
	 * @return 转化后数据
	 */
	public static String convertU62Num(String numStr) {
		int num = 0;
		try {
			num = Integer.parseInt(numStr);
		} catch (NumberFormatException ex) {
			throw new IllegalArgumentException("parseInt失败，传入的" + numStr + "非法");
		}
		if (num > CONVERT_NUM_ARR.length) {
			throw new IllegalArgumentException("传入的参数不可大于62");
		}
		return CONVERT_NUM_ARR[num];
	}

	/**
	 * Get appointed file content according to the <code>filePath</code>.
	 * This method will try read the file in following sequence:
	 * <ul>
	 * <li>1. From classpath.
	 * <li>2. From file system, if 1 failed.
	 * </ul>
	 * So, <code>filePath</code> can be one of following forms:
	 * <ul>
	 * <li>1. Prefixed with <code>classpath:</code> indicate a classpath
	 * resource.
	 * <li>2. Prefixed with <code>file:</code> indicate an absolute file path
	 * resource.
	 * <li>3. Plain paths will always be interpreted as relative to the current
	 * VM working directory, even if they start with a slash.
	 * </ul>
	 *
	 * @param filePath
	 *            the path of the file.
	 * @return file content.
	 * @throws FileNotFoundException
	 *             if appointed file can not be read or not exist.
	 */
	public static byte[] getFile(String filePath) throws FileNotFoundException {
		try {
			ResourceLoader resourceLoader = new DefaultResourceLoader();
			Resource resource = resourceLoader.getResource(filePath);
			InputStream inputStream = null;
			if (resource.exists()) {
				inputStream = resource.getInputStream();
			} else {
				resourceLoader = new FileSystemResourceLoader();
				resource = resourceLoader.getResource(filePath);
				if (resource.exists()) {
					inputStream = resource.getInputStream();
				} else {
					inputStream = new FileInputStream(filePath);
				}
			}
			byte[] fileByte = new byte[inputStream.available()];
			int fileLength = inputStream.read(fileByte);
			inputStream.close();
			if (fileLength >= 0) {
				byte[] targetFile = new byte[fileLength];
				System.arraycopy(fileByte, 0, targetFile, 0, fileLength);
				return targetFile;
			} else {
				throw new FileNotFoundException("File: [" + filePath
						+ "] can not be read or is empty");
			}
		} catch (Exception e) {
			if (e instanceof FileNotFoundException) {
				throw (FileNotFoundException) e;
			}
			throw new FileNotFoundException("File: [" + filePath
					+ "] can not be read");
		}
	}

	/**
	 * Get the canonical file path according to the relative or classpath file
	 * path.
	 *
	 * @param filePath
	 *            relative or classpath file path.
	 * @return canonical file path, or return <code>filePath</code> if
	 *         designated file does not exist or can not be read due to
	 *         permission.
	 */
	public static String getCanonicalFilePath(String filePath) {
		try {
			ResourceLoader resourceLoader = new DefaultResourceLoader();
			Resource resource = resourceLoader.getResource(filePath);
			File file = null;
			if (resource.exists()) {
				file = resource.getFile();
			} else {
				resourceLoader = new FileSystemResourceLoader();
				resource = resourceLoader.getResource(filePath);
				if (resource.exists()) {
					file = resource.getFile();
				} else {
					return filePath;
				}
			}
			if (file != null && file.exists() && file.isFile()) {
				String absFilePath = file.getCanonicalPath();
				return absFilePath;
			} else {
				return filePath;
			}
		} catch (Exception e) {
			return filePath;
		}
	}

	/**
	 * Get the canonical file path according to the relative or classpath file
	 * path.
	 *
	 * @param filePath
	 *            relative or classpath file path.
	 * @param ignoreFileExist
	 *            <code>true</code> indicate whether file exists return
	 *            canonical path of the file.
	 * @return canonical file path, or return <code>filePath</code> if
	 *         designated file does not exist or can not be read due to
	 *         permission.
	 */
	public static String getCanonicalFilePath(String filePath,
			boolean ignoreFileExist) {
		if (!StringUtils.hasLength(filePath)) {
			throw new IllegalArgumentException(
					"filePath can not be null or empty");
		}
		if (!ignoreFileExist) {
			String cannonicalFilePath = TransUtil
					.getCanonicalFilePath(filePath);
			return cannonicalFilePath;
		}
		final int forwardSlash = 0x2F;
		final int backSlash = 0x5C;
		final int begin = 0;
		final int step = 1;
		String directory = null;
		if (filePath.lastIndexOf(forwardSlash) != -1) {
			directory = filePath.substring(begin, filePath
					.lastIndexOf(forwardSlash)
					+ step);
		} else if (filePath.lastIndexOf(backSlash) != -1) {
			directory = filePath.substring(0, filePath.lastIndexOf(backSlash)
					+ step);
		} else {
			directory = filePath;
		}
		Resource resource = new ClassPathResource(directory);
		try {
			File file = resource.getFile();
			StringBuffer path = new StringBuffer(file.getCanonicalPath());
			if (directory.length() != filePath.length()) {
				path.append(File.separator).append(
						filePath.substring(directory.length()));
			}
			String pathStr = path.toString();
			return pathStr;
		} catch (IOException e) {
			return filePath;
		}
	}

	/**
	 * 为银行定制12位订单号.(国付宝默认16位).
	 *
	 * @param orderId orderId
	 * @return String
	 * @throws Exception
	 */
	public static String processOrderIdTo12Digit(String orderId) {
		// 订单号（规定12位，要进行处理）
		// 分别截取年月日
		String yearStr = orderId.substring(2, Constants.NUMBER_4);
		String monthStr = orderId.substring(Constants.NUMBER_4, Constants.SIX);
		String dayStr = orderId.substring(Constants.SIX, Constants.NUMBER_8);
		// 截取订单号
		String cebOrderId = yearStr + convertU62Num(monthStr)
				+ convertU62Num(dayStr)
				+ orderId.substring(Constants.NUMBER_8);

		return cebOrderId;
	}

	/**
	 * 为银行定制15位订单号.(国付宝默认16位).
	 *
	 * @param orderId orderId
	 * @return String
	 * @throws Exception
	 */
	public static String processOrderIdTo15Digit(String orderId) {
		// 订单号（规定15位，要进行处理）
		// 截取月
		String monthStr = orderId.substring(Constants.NUMBER_4, Constants.SIX);

		// 截取订单号
		orderId = orderId.substring(0, Constants.NUMBER_4) + convertU62Num(monthStr)
				+ orderId.substring(Constants.SIX);

		return orderId;
	}

	/**
	 * 为银行定制8位订单号.(国付宝默认16位).
	 *
	 * @param orderId orderId
	 * @return String
	 * @throws Exception
	 */
	public static String processOrderIdTo8Digit(String orderId) {
		// 订单号（规定8位，要进行处理）
		// 截取订单号（将年月日信息截掉只使用系统生成的NextID）
		orderId = orderId.substring(Constants.NUMBER_8);
		return orderId;
	}

	/**
	 * 为银行定制10位订单号.(国付宝默认16位).
	 *
	 * @param orderId orderId
	 * @return String
	 * @throws Exception
	 */
	public static String processOrderIdTo10Digit(String orderId) {
		// 订单号（规定10位，要进行处理）
		// 分别截取年月
		String yearStr = orderId.substring(2, Constants.NUMBER_4);
		String monthStr = orderId.substring(Constants.NUMBER_4, Constants.SIX);
		// 截取订单号
		String cebOrderId = convertU62Num(yearStr) + convertU62Num(monthStr)
				+ orderId.substring(Constants.NUMBER_8);

		return cebOrderId;
	}
	
	/**
	 * 为银行定制14位订单号.(国付宝默认16位)
	 * 
	 * @param orderId
	 * @return String
	 * @throws Exception
	 */
	public static String processOrderIdTo14Digit(String orderId) {
		// 订单号（规定14位，要进行处理）
		// 将年的前两位截掉
		
		orderId = orderId.substring(2);

		return orderId;
	}	
	
	/**
     * Fetch designated field value in the message body according to the
     * appointed tag pair. If there are more than one open tag or close tag,
     * then return the string value between first open tag and first close tag;
     * or throw <code>TransformException</code>.
     * 
     * @param beginTag
     *            起始标签.
     * @param endTag
     *            结束标签.
     * @param msg
     *            Message body as <code>String</code>.
     * @return String 域值.
     * @throws TransformException
     *             缺少指定的起始或结束标签.
     */
    public static String getMsgField(String beginTag, String endTag, String msg) {
        int beginIndex = msg.indexOf(beginTag);
        int endIndex = msg.indexOf(endTag);
        if (beginIndex == -1 || endIndex == -1) {
            throw new RuntimeException("Unintegrated message, with absence of " + beginTag);
        }
        String returnCode = msg.substring(beginIndex + beginTag.length(), endIndex);
        return returnCode;
    }

	/**
	 * 判断字符串是否有值.
	 *
	 * @param str str
	 * @return boolean
	 */
	public static boolean isStringBlank(String str) {
		return (str == null || "".equals(str));
	}

	/** 金额表达式. */
	private static final String AMOUNT_REGULAR_EX = "^(\\d{1,10}(\\.\\d{0,2})?)$";
	/** 金额模式. */
	private static final Pattern PATTERN = Pattern.compile(AMOUNT_REGULAR_EX);
	/** 100, 工行金额放大一百倍. */
	private static final BigDecimal ONEHUNDRED = new BigDecimal(100);
	/** Integer 0. */
	public static final Integer LOCK = new Integer(0);
	/** 两位. */
	private static final int TWO_BIT = 10;
	/** 三位. */
	private static final int THREE_BIT = 100;
	/** 长度. */
	private static final int LENGTH = 1000;
	/** Suffix. */
	private static int suffix = 0;
	/** 插一个零. */
	private static final char SUFFIX_ONE_ZERO = '0';
	/** 插两个零. */
	private static final String SUFFIX_TWO_ZERO = "00";
	/** 时间格式yyyyMMddHHmmss. */
	private static final String TIME_FORMAT_YEAR_SECOND = "yyyyMMddHHmmss";
	/** 时间格式yyyyMMddHHmmssSSS. */
	private static final String TIME_FORMAT_YEAR_MILLISECOND = "yyyyMMddHHmmssSSS";
	/** 时间格式yyyyMMddHHmmSSS. */
	private static final String TIME_FORMAT_YEAR_SECOND_F = "yyyyMMddHHmmSSS";
	/** 时间格式yyMMddHHmmSSS. */
	private static final String TIME_FORMAT_TWO_BIT_YEAR_SECOND = "yyMMddHHmmss";
	/** 时间格式yyyy-MM-dd HH:mm:ss. */
	private static final String TIME_FORMAT_YEAR_SECOND_G = "yyyy-MM-dd HH:mm:ss";
	/** 时间格式yyyy-MM-dd. */
	private static final String TIME_FORMAT_YEAR_G = "yyyy-MM-dd";
	/** 时间格式yyyyMMdd. */
	private static final String TIME_FORMAT_YEAR_Y = "yyyyMMdd";
	/** 时间格式yyyyMMdd. */
	private static final String CURRENT_DATE = "yyyyMMdd";
	/**
	 * 用于格式化62以下数字-->个位数字+字母.
	 */
	private static final String[] CONVERT_NUM_ARR = new String[] {"0", "1",
			"2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E",
			"F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R",
			"S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e",
			"f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r",
			"s", "t", "u", "v", "w", "x", "y", "z" };

}
